package fr.umlv.pirates;

import java.util.Observable;
import java.util.Timer;
import java.util.TimerTask;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.PointF;
import android.graphics.RectF;
import fr.umlv.physics.Force;
import fr.umlv.physics.LinearMotion;
import fr.umlv.physics.PhysicalObject;
import fr.umlv.physics.World;
import fr.umlv.utils.VectorUtil;

public class Pirate extends Observable implements Renderable, PhysicalObject{
	
	public static final int STATE_FALLING		= 0;
	public static final int STATE_STUCK 		= 1;
	public static final int CONTACT_TOLERANCE	= 250;
	public static final int EXTRASPEED_LENGHT	= 2000;
	
	public static final float JUMP_NORMAL 		= 100;
	
	boolean displayHardTouchZone;
	boolean extraSpeed;
	boolean hasStuck;
	boolean isAlive;
	boolean isInvincible;
	boolean isContactEnabled;
	int gravityDirection;
	int lives;
	int mass;
	int state;
	float speed;
	float width;
	Object lock;
	String name;
	RectF rect;
	RectF modeHardTouchZone;
	Timer bonusTimer;
	Timer speedTimer;
	LinearMotion currentLinearMotion;
	Force currentForce;	
	Paint paint;
	
	public Pirate(String name, float left, float top, float width){
		this(name, new RectF(left, top, left + width, top + width));
	}
	
	public Pirate(String name, RectF rect){
		
		this.displayHardTouchZone	= false;
		this.extraSpeed				= false;
		this.hasStuck				= false;
		this.isAlive		 		= true;
		this.isInvincible			= false;
		this.isContactEnabled		= true;
		this.gravityDirection		= TOP_TO_BOTTOM;
		this.mass					= 40;
		this.speed					= 0f;
		this.width					= rect.width();
		this.lock					= new Object();
		this.name 					= name;
		this.rect 					= rect;
		this.modeHardTouchZone		= new RectF();
		this.bonusTimer				= new Timer();
		this.speedTimer				= new Timer();
		this.paint					= new Paint();
		
		float hardWidth				= (float) (rect.width() * 1.5);
		modeHardTouchZone.left 		= rect.left - hardWidth;
		modeHardTouchZone.top 		= rect.top - hardWidth;
		modeHardTouchZone.right 	= rect.right + hardWidth;
		modeHardTouchZone.bottom	= rect.bottom + hardWidth;
	}
	
	public void jumpOrSpeed(){
		
		if (state == STATE_FALLING) return;

		if (hasStuck){
			extraSpeed = true;
			speedTimer.schedule(new TimerTask() {
				
				@Override
				public void run() {
					extraSpeed = false;
				}
			}, EXTRASPEED_LENGHT);
			
		}else{
			state = STATE_FALLING;
			
			SoundManager.getInstance().playJump();
			
			if (currentLinearMotion == null){
				if (gravityDirection == PhysicalObject.BOTTOM_TO_TOP || gravityDirection == PhysicalObject.TOP_TO_BOTTOM)
					currentForce = new Force(this, 0, JUMP_NORMAL);
				else if (gravityDirection == PhysicalObject.LEFT_TO_RIGHT || gravityDirection == PhysicalObject.RIGHT_TO_LEFT)
					currentForce = new Force(this, JUMP_NORMAL, 0);
			}else{
				if (gravityDirection == PhysicalObject.BOTTOM_TO_TOP || gravityDirection == PhysicalObject.TOP_TO_BOTTOM){
					if (currentLinearMotion.getWay() == LinearMotion.WAY_LEFT_TOP)
						currentForce = new Force(this, -(currentLinearMotion.getVelocity() * mass), JUMP_NORMAL);
					else
						currentForce = new Force(this, currentLinearMotion.getVelocity() * mass, JUMP_NORMAL);
						
				}else if (gravityDirection == PhysicalObject.LEFT_TO_RIGHT || gravityDirection == PhysicalObject.RIGHT_TO_LEFT){
					if (currentLinearMotion.getWay() == LinearMotion.WAY_LEFT_TOP)
						currentForce = new Force(this, JUMP_NORMAL, currentLinearMotion.getVelocity() * mass);
					else
						currentForce = new Force(this, JUMP_NORMAL, -(currentLinearMotion.getVelocity() * mass));
				}					
			}
		}
		
	}
	
	public void setTopLeft(float x, float y){
		
		float height	= rect.height();
		float width 	= rect.width();
		float hardWidth	= (float) (width * 1.5);
		
		rect.left	= x;
		rect.top	= y;
		rect.right	= x + width;
		rect.bottom	= y + height;
		
		modeHardTouchZone.left 		= rect.left - hardWidth;
		modeHardTouchZone.top 		= rect.top - hardWidth;
		modeHardTouchZone.right 	= rect.right + hardWidth;
		modeHardTouchZone.bottom	= rect.bottom + hardWidth;
	}

	public PointF getOrigin(){
		return new PointF(rect.left, rect.top); 
	}

	public boolean isDisplayHardTouchZone() {
		return displayHardTouchZone;
	}
	
	public boolean isExtraSpeeding(){
		return extraSpeed;
	}

	public void setDisplayHardTouchZone(boolean displayHardTouchZone) {
		this.displayHardTouchZone = displayHardTouchZone;
	}

	public boolean isContactEnabled() {
		return isContactEnabled;
	}

	public void setContactEnabled(boolean isContactEnabled) {
		
		synchronized (lock) {
			this.isContactEnabled = isContactEnabled;
		}
		
		
		if (!isContactEnabled){
			speedTimer.schedule(new TimerTask() {
				
				@Override
				public void run() {
					Pirate.this.isContactEnabled = true;
				}
			}, 2000);
		}
	}
	
	public void setInvincible(){
		
		isInvincible = true;
		
		bonusTimer.schedule(new TimerTask() {
			
			@Override
			public void run() {
				isInvincible = false;
			}
		}, 10000);
	}
	
	public void setBigger(){
		
		rect = new RectF(rect.left, rect.top, rect.left + (width * 1.5f), rect.top + (width * 1.5f));
		
		bonusTimer.schedule(new TimerTask() {
			
			@Override
			public void run() {
				rect = new RectF(rect.left, rect.top, rect.left + width, rect.top + width);
			}
		}, 10000);		
	}

	public int getState() {
		return state;
	}

	public void setState(int state) {

		this.state = state;

		if (state == Pirate.STATE_STUCK){
			
			hasStuck = true;
			
			speedTimer.schedule(new TimerTask() {

				@Override
				public void run() {
					hasStuck = false;
				}
			}, CONTACT_TOLERANCE);
		}
	}

	public void setGravityDirection(int gravityDirection) {
		this.gravityDirection = gravityDirection;
	}

	public int getLives() {
		return lives;
	}

	public void decreaseLives() {
		
		lives--;
		
		if (lives <= 0)
			isAlive = false;
		
		setChanged();
		notifyObservers(new LiveChange(this, LiveChange.DECREASE));
	}
	
	public void increaseLives() {
		
		lives++;
		
		setChanged();
		notifyObservers(new LiveChange(this, LiveChange.INCREASE));
	}
	
	public int getMass() {
		return mass;
	}

	public void setMass(int mass) {
		this.mass = mass;
	}

	public float getSpeed() {
		return speed;
	}

	public void setSpeed(float speed) {
		this.speed = speed;
	}

	public String getName(){
		return name;
	}
	
	@Override
	public RectF getRect() {
		return rect;
	}

	public void setRect(RectF rect) {
		this.rect = rect;
	}

	public Force getCurrentForce() {
		return currentForce;
	}
	
	public LinearMotion getCurrentLinearMotion() {
		return currentLinearMotion;
	}
	
	@Override
	public void createPhysics(World world) {
		world.addPirate(this);
	}

	@Override
	public void applyForce(Force force) {
		
		synchronized (lock) {
			this.currentForce = force;	
		}
	}
	
	@Override
	public void applyLinearMotion(LinearMotion linearMotion) {
		
		synchronized (lock) {
			this.currentLinearMotion = linearMotion;
		}
	}

	@Override
	public void clearForce() {
		
		synchronized (lock) {
			this.currentForce = null;
		}
	}
	
	@Override
	public void clearLinearMotion() {
		synchronized (lock) {
			this.currentLinearMotion = null;
		}
	}
	
	@Override
	public int getGravityDirection() {
		return gravityDirection;
	}
	
	@Override
	public boolean equals(Object o) {

		if (o instanceof Pirate){
			Pirate p = (Pirate)o;
			
			return name.equals(p.name);
		}
		return false;
	}
	
	@Override
	public void draw(Canvas canvas){
		
		Path path = new Path();
		float w = rect.width();
		path.addCircle((rect.left + w / 2), (rect.top + w / 2), w * 2, Path.Direction.CW);
		
		canvas.save();
		paint.setAntiAlias(true);
		if (!isContactEnabled)
			paint.setAlpha(128);
		else
			paint.setAlpha(255);
		
		if (isInvincible){
			int previousColor = paint.getColor();
			paint.setPathEffect(new PathEffect());
			paint.setStyle(Style.FILL);
			paint.setColor(Color.BLUE);
			canvas.drawPath(VectorUtil.getInvicibleBonusPath(rect), paint);
			paint.setStyle(Style.STROKE);
			paint.setColor(previousColor);
		}
		else
			paint.setStyle(Style.FILL);
		canvas.drawOval(rect, paint);
		
		if (displayHardTouchZone){
			paint.setStyle(Paint.Style.STROKE);
			paint.setStrokeWidth(4);
			paint.setAlpha(255);
			paint.setPathEffect(new DashPathEffect(new float[]{10f, 10f},  50f));
			canvas.drawPath(path, paint);
		}
		
		canvas.restore();
	}
}
